﻿/******************************************************
* author :  cwj
* email  :  chenwenji_360@live.com 
* history:  created by cwj 2015/7/16 16:26:53 
* clrversion :4.0.30319.18444
******************************************************/

using Machine.DataAccess.Common.ORM;
using Machine.DataAccess.Operation;
using System;
using System.Collections;
using System.Collections.Generic;
using System.Data;
using System.Data.Common;
using System.Data.SqlClient;
using System.Linq;
using System.Reflection;
using System.Text;

namespace Machine.DataAccess.Linq
{
    class DataReader<T> : ITable<T>, IEnumerable<T>
    {
        //Enumerator enumerator;
        //string cmdCommand;
        //string connectionStr;
        TranslateResult translateResult;
        CommandType commandType;
        ProviderElement provider;
        object parent;

        public DataReader(TranslateResult result, ProviderElement provider,object parent, CommandType commandType = CommandType.Text)
        {
            //this.cmdCommand = result.SqlText;
            this.translateResult = result;
            this.commandType = commandType;
            this.provider = provider;
            this.parent = parent;
            //this.connectionStr = provider.ConnectionString;//Config.Instance.DefaultProvider.ConnectionString;//ConfigurationManager.ConnectionStrings["test"].ConnectionString;
            //enumerator = new Enumerator(ConfigurationManager.ConnectionStrings["test"].ConnectionString, cmdCommand);
        }

        public IEnumerator<T> GetEnumerator()
        {
            var type = typeof(T);
            return new Enumerator(this.provider, this.translateResult, this.commandType);
        }

        IEnumerator IEnumerable.GetEnumerator()
        {
            return this.GetEnumerator();
        }

        public bool Add(T entity)
        {
            var schem = Config.Instance.GetDbSchemInfo(this.provider);
            var operation = Config.Instance.GetDataSetOperator<T>(this.provider) as DataSetOperatorBase<T>;

            var properties = operation.GetProperties(entity.GetType(), entity, schem);
            var temp = this.GetCoumFKey(schem);
            var fkCoum = properties.FirstOrDefault(x => x.CoumName == temp.CoumName);
            if (fkCoum != null) properties.Remove(fkCoum);
            properties.Add(temp);
            return operation.AddEntity(entity, properties, schem);
        }

        public bool Delete(T entity)
        {
            var operation = Config.Instance.GetDataSetOperator<T>(this.provider) as DataSetOperatorBase<T>;
            return operation.Delete(entity);
        }

        public bool Update(T entity)
        {
            var operation = Config.Instance.GetDataSetOperator<T>(this.provider) as DataSetOperatorBase<T>;
            return operation.Update(entity);
        }

        //public bool AddOrUpdate(T entity)
        //{
        //    var operation = Config.Instance.GetDataSetOperator<T>(this.provider) as DataSetOperatorBase<T>;
        //    throw new NotImplementedException();
        //}

        private CoumInfo GetCoumFKey(DbSchemInfoBase schem)
        {
            var pkName = schem.GetPrimaryKey(this.parent.GetType().GetTableName());
            var propertyInvoker = DynamicAssignment.Instance.GetDictionary(this.parent.GetType())[pkName.ToLower()];

            var relation = schem.GetTableRelationType(this.parent.GetType(), typeof(T));
            var outterKey = schem.GetOuterFKey(this.parent.GetType(), typeof(T));

            switch (relation)
            {
                case TableRelationType.Zero2One:
                case TableRelationType.Zero2Many:
                case TableRelationType.One2Zero:
                case TableRelationType.One2One:
                case TableRelationType.Zero2Zero:
                    return null;
                case TableRelationType.Many2Zero:
                case TableRelationType.Many2One:
                case TableRelationType.One2Many:
                    return new CoumInfo()
                    {
                        CoumName = outterKey.Item2.First(x=>x.Table.ToLower() == this.parent.GetType().GetTableName()).From,
                        PropertyInvoker = propertyInvoker,
                        Entity = this.parent
                    };
                case TableRelationType.Many2Many:
                default:
                    throw new NotSupportedException("暂时不支持");
            }
        }

        private class Enumerator : IEnumerator<T>
        {
            IDataReader reader;
            DbConnection con;
            DbCommand command;
            OrmType ormType;
            Type type;
            PropertyInfo[] properties;
            ProviderElement providerElement;
            private object _current;

            public Enumerator(ProviderElement providerElement, TranslateResult translate, CommandType commandType = CommandType.Text)
            {
                type = typeof(T);
                this.providerElement = providerElement;
                this.ormType = new OrmHelper().GetTypeCode(type);
                if (this.ormType == OrmType.UnKnowType)
                {
                    //properties = type.GetProperties(); ;
                    throw new NotSupportedException();
                }
                //var schem = Config.Instance.GetDbSchemInfo(this.providerElement);
                //var p = schem.GetSchem(type).Colunms;

                var factory = DbProviderFactories.GetFactory(providerElement.Provider);
                con = factory.CreateConnection(); //new SqlConnection(connectionString);
                con.ConnectionString = providerElement.ConnectionString;
                con.Open();
                command = con.CreateCommand();
                command.CommandText = translate.SqlText;
                command.CommandType = commandType;

                command.Parameters.AddRange(translate.GetParameters(command).ToArray());
                this.reader = command.ExecuteReader(CommandBehavior.CloseConnection);
            }

            public T Current { get { return (T)_current; } }

            public void Dispose() { reader.Dispose(); command.Dispose(); con.Dispose(); }

            object IEnumerator.Current { get { return this.Current; } }

            public bool MoveNext()
            {
                if (this.reader != null && this.reader.Read())
                {
                    if (type == typeof(object))
                    {
                        this._current = new System.Dynamic.ExpandoObject();
                        var obj = this._current as IDictionary<string, object>;
                        var count = this.reader.FieldCount;
                        var index = 0;
                        while (index < count)
                        {
                            obj[this.reader.GetName(index)] = this.reader.GetValue(index);
                            index += 1;
                        }
                    }
                    else if(type.Name.StartsWith("<>"))
                    {
                        var obj = new object[this.reader.FieldCount];
                        this.reader.GetValues(obj);
                        this._current = Activator.CreateInstance(type, obj);
                    }
                    else
                    {
                        this._current = new OrmHelper().GetResult<T>(reader, providerElement, this.ormType);
                        //if (this._current == null) return false;
                    }
                    return true;
                }
                return false;
            }


            public void Reset() { throw new NotSupportedException("不支持重置"); }
        }
    }
}
